//
//  FZDubEvaluationManager.m
//  KidDubbing
//
//  Created by Victor Ji on 2016/11/9.
//  Copyright © 2016年 Feizhu Tech. All rights reserved.
//

#import "SREvaluationManager.h"
#import <IflyMSC/IflyMSC.h>
//#import "aiengine.h"
//#import "airecorder.h"
#import "ISEResultXmlParser.h"
#import "ISEResult.h"
#import "ISEResultWord.h"
#import "ISEResultSentence.h"
#import "SREvaluationScoreBonusModel.h"

@interface SREvaluationManager () //<IFlySpeechEvaluatorDelegate, ISEResultXmlParserDelegate>
{
//    struct airecorder *aiRecorder;
//    struct aiengine *aiEngine;
}

@property (copy, nonatomic) NSString *aiConfigString;

@property (strong, nonatomic) IFlySpeechEvaluator *iflyEvaluator;
@property (strong, nonatomic) ISEResultXmlParser *iflyXMLParser;

@property (assign, nonatomic) FZDubEvaluationSupplier supplier;
@property (assign, nonatomic) SRSpeakingEvaluationTextType language;

@property (copy, nonatomic) NSArray *bonusArray;

@property (assign, nonatomic) BOOL resultNotOK;


@end

@implementation SREvaluationManager

+ (SREvaluationManager *)sharedManager {
    static SREvaluationManager *stance;
    static dispatch_once_t once_t;
    dispatch_once(&once_t, ^{
        stance = [[SREvaluationManager alloc] init];
    });
    return stance;
}

- (void)setSupplier:(FZDubEvaluationSupplier)supplier {
    _supplier = supplier;
    
    if (supplier == FZDubEvaluationSupplierIfly) {
        [self createIflyEvaluatorWithLanguage:self.language];
    } else {
        [self createChivoxEvaluator];
    }
}

//const char *aiengine_helper_extract_provision_once(const char *name)
//{
//    /* there's no need to extract, return path directly */
//    static char provision[1024];
//    strcpy(provision, [[[NSBundle mainBundle] pathForResource:[[NSString alloc] initWithUTF8String:name] ofType:NULL] UTF8String]);
//    
//    printf("provision profile: %s\n", provision);
//    return provision;
//}
//
//static int _recorder_callback(const void * usrdata, const void * data, int size)
//{
//    printf("feed: %d\n", size);
//    return aiengine_feed((struct aiengine *) usrdata, data, size);
//}
//
//static int _aiengine_callback(const void *usrdata, const char *id, int type, const void *message, int size)
//{
//    if (type == AIENGINE_MESSAGE_TYPE_JSON) {
//        NSString *result = [[NSString alloc] initWithUTF8String:(char *)message];
//        [(__bridge FZDubEvaluationManager *)usrdata performSelectorOnMainThread:@selector(parseChivoxResult:) withObject:result waitUntilDone:NO];
//    }
//    return 0;
//}

- (void)createChivoxEvaluator {
//    if (!self.aiConfigString || [self.aiConfigString isEqualToString:@""]) {
//        self.aiConfigString = @"{\"coreProvideType\": \"cloud\",\"app\": {\"userId\": \"this-is-user-id\"}, \"audio\": {\"audioType\": \"wav\", \"sampleRate\": 16000, \"channel\": 1, \"sampleBytes\": 2}, \"request\" : {\"coreType\": \"en.sent.score\",  \"rank\": 100, \"attachAudioUrl\": 1, \"refText\":";
//    }
//    
//    if (!aiEngine) {
//        char app_key[64];
//        char secret_key[64];
//        char user_id[64];
//        char cfg[4096];
//        const char *provision;
//        
//        strcpy(app_key, "147858674500006b");
//        strcpy(secret_key, "5946ace35770d2ae7bdacdbcc3fed197");
//        strcpy(user_id, "com.ishowtalk.dubbing");
//        
//        provision = aiengine_helper_extract_provision_once("aiengine.provision");
//        
//        sprintf(cfg, "{\"appKey\": \"%s\", \"secretKey\": \"%s\", \"provision\": \"%s\",\"cloud\":{\"enable\":1, \"server\": \"ws://cloud.chivox.com:8080\"}}", app_key, secret_key, provision);
//        
//        aiEngine = aiengine_new(cfg);
//    }
//    
//    if (!aiRecorder) {
//        aiRecorder = airecorder_new();
//    }
//    
//    
//    if (![[NSFileManager defaultManager] fileExistsAtPath:[NSHomeDirectory() stringByAppendingFormat:@"/Documents/airecord"]]) {
//        [[NSFileManager defaultManager] createDirectoryAtPath:[NSHomeDirectory() stringByAppendingFormat:@"/Documents/airecord"] withIntermediateDirectories:YES attributes:nil error:nil];
//    }
}

- (void)createIflyEvaluatorWithLanguage:(SRSpeakingEvaluationTextType)language {
    if (!self.iflyEvaluator) {
        self.iflyEvaluator = [IFlySpeechEvaluator sharedInstance];
        [self.iflyEvaluator setParameter:@"" forKey:[IFlySpeechConstant PARAMS]];
        [self.iflyEvaluator setParameter:@"16000" forKey:[IFlySpeechConstant SAMPLE_RATE]];
        [self.iflyEvaluator setParameter:@"utf-8" forKey:[IFlySpeechConstant TEXT_ENCODING]];
        [self.iflyEvaluator setParameter:@"xml" forKey:[IFlySpeechConstant ISE_RESULT_TYPE]];
        [self.iflyEvaluator setParameter:@"0" forKey:[IFlySpeechConstant VAD_ENABLE]];
        //    [self.evaluator setParameter:@"5000" forKey:[IFlySpeechConstant VAD_BOS]];
        //    [self.evaluator setParameter:@"1800" forKey:[IFlySpeechConstant VAD_EOS]];
        [self.iflyEvaluator setParameter:@"read_sentence" forKey:[IFlySpeechConstant ISE_CATEGORY]];
        if (language == SRSpeakingEvaluationTextTypeChinese) {
            [self.iflyEvaluator setParameter:@"zh_cn" forKey:[IFlySpeechConstant LANGUAGE]];
        } else {
            [self.iflyEvaluator setParameter:@"en_us" forKey:[IFlySpeechConstant LANGUAGE]];
        }
        [self.iflyEvaluator setParameter:@"complete" forKey:[IFlySpeechConstant ISE_RESULT_LEVEL]];
        [self.iflyEvaluator setParameter:@"-1" forKey:[IFlySpeechConstant SPEECH_TIMEOUT]];
        [self.iflyEvaluator setParameter:@"utf-8" forKey:[IFlySpeechConstant RESULT_ENCODING]];
        [self.iflyEvaluator setParameter:@"5000" forKey:[IFlySpeechConstant NET_TIMEOUT]];
        [self.iflyEvaluator setParameter:[SRLoginUser userID] forKey:@"auth_id"];
        [self.iflyEvaluator setParameter:@"0" forKey:@"plev"];
        self.iflyEvaluator.delegate = self;
    }
    
    if (!self.iflyXMLParser) {
        self.iflyXMLParser = [[ISEResultXmlParser alloc] init];
        self.iflyXMLParser.delegate = self;
    }
    
    if (!self.iflyEvaluator.delegate) {
        self.iflyEvaluator.delegate = self;
    }
    if (!self.iflyXMLParser.delegate) {
        self.iflyXMLParser.delegate = self;
    }
}

- (void)setEvaluationSupplier:(FZDubEvaluationSupplier)supplier language:(SRSpeakingEvaluationTextType)language {
    self.language = language;
    self.supplier = supplier;
}

- (void)queryEvaluationSupplierFromServerWithDefaultSupplier:(FZDubEvaluationSupplier)supplier {
    self.supplier = supplier;
}

- (void)startRecordWithText:(NSString *)text textType:(SRSpeakingEvaluationTextType)textType bonusArray:(NSArray *)bonusArray; {
    if (self.resultNotOK) {
        return;
    }
    
    self.resultNotOK = YES;
    
    if (bonusArray && bonusArray.count > 0) {
        self.bonusArray = bonusArray;
    } else {
        self.bonusArray = @[];
    }
    
    if ([self.delegate respondsToSelector:@selector(speakingEvaluationStatusChanged:message:)]) {
        [self.delegate speakingEvaluationStatusChanged:SRSpeakingEvaluationStatusBegin message:@"语音测评器-开始测评"];
    }
    if (self.supplier == FZDubEvaluationSupplierIfly) {
        NSData *data = nil;
        if (textType == SRSpeakingEvaluationTextTypeEnglish) {
            data = [text dataUsingEncoding:NSUTF8StringEncoding];
        } else if (textType == SRSpeakingEvaluationTextTypeChinese) {
            Byte bomHeader[] = { 0xEF, 0xBB, 0xBF };
            NSMutableData *buffer = [NSMutableData dataWithBytes:bomHeader length:sizeof(bomHeader)];
            NSData *originData = [text dataUsingEncoding:NSUTF8StringEncoding];
            [buffer appendData:originData];
            data = [NSData dataWithData:buffer];
        }
        if (data) {
            [self.iflyEvaluator startListening:data params:nil];
        } else {
            return;
        }
    } else if (self.supplier == FZDubEvaluationSupplierChivox) {
        if (textType == SRSpeakingEvaluationTextTypeChinese) {
            
            return;
        }
//        NSString *finalText = text;
//        if ([text containsString:@"\""] || [text containsString:@"\\"]) {
//            NSString *final1Text = [text stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
//            finalText = final1Text;
//        }
//        NSString *string = [NSString stringWithFormat:@"%@\"%@\"}}", self.aiConfigString, finalText];
//        const char *configStr = [string cStringUsingEncoding:NSUTF8StringEncoding];
//        int returnValue = 0;
//        char record_id[64] = {0};
//        returnValue = aiengine_start(aiEngine, configStr, record_id, (aiengine_callback)_aiengine_callback, (__bridge const void *)self);
//        if (returnValue != 0) {
//            if ([self.delegate respondsToSelector:@selector(speakingEvaluationStatusChanged:message:)]) {
//                [self.delegate speakingEvaluationStatusChanged:SRSpeakingEvaluationStatusStartRecordFail message:@"语音测评器-启动录音失败"];
//            }
//        } else {
//            if ([self.delegate respondsToSelector:@selector(speakingEvaluationStatusChanged:message:)]) {
//                [self.delegate speakingEvaluationStatusChanged:SRSpeakingEvaluationStatusStartRecord message:@"语音测评器-开始录音"];
//            }
//            
//            if ([self.delegate respondsToSelector:@selector(dubEvaluationRecordToken:)]) {
//                NSString *tokenID = [NSString stringWithUTF8String:record_id];
//                [self.delegate dubEvaluationRecordToken:tokenID];
//            }
//            
//            char wav_path[1024];
//            sprintf(wav_path, "%s/Documents/airecord/%s.wav", [NSHomeDirectory() UTF8String], record_id);
//            
//            int rv = 0;
//            rv = airecorder_start(aiRecorder, wav_path, _recorder_callback, aiEngine, 100);
//            if(rv != 0) {
//                printf("airecorder_start() failed: %d\n", rv);
//            } else {
//                printf("airecorder_start() success");
//            }
//        }
    }
}

- (void)stopRecord {
    self.resultNotOK = NO;
    
    if (self.supplier == FZDubEvaluationSupplierIfly) {
        [self.iflyEvaluator stopListening];
    } else if (self.supplier == FZDubEvaluationSupplierChivox) {
//        int rv = 0;
//        rv = airecorder_stop(aiRecorder);
//        int returnValue = 0;
//        returnValue = aiengine_stop(aiEngine);
//        if (returnValue != 0 || rv != 0) {
//            if ([self.delegate respondsToSelector:@selector(speakingEvaluationStatusChanged:message:)]) {
//                [self.delegate speakingEvaluationStatusChanged:SRSpeakingEvaluationStatusStopRecordFail message:@"语音测评器-结束录音失败"];
//            }
//        } else {
//            if ([self.delegate respondsToSelector:@selector(speakingEvaluationStatusChanged:message:)]) {
//                [self.delegate speakingEvaluationStatusChanged:SRSpeakingEvaluationStatusStopRecord message:@"语音测评器-结束录音"];
//            }
//        }
    }
}

#pragma mark - ifly delegate

- (void)onVolumeChanged:(int)volume buffer:(NSData *)buffer {
    
}

- (void)onBeginOfSpeech {
    if ([self.delegate respondsToSelector:@selector(speakingEvaluationStatusChanged:message:)]) {
        [self.delegate speakingEvaluationStatusChanged:SRSpeakingEvaluationStatusStartRecord message:@"语音测评器-开始录音"];
    }
}

- (void)onEndOfSpeech {
    if ([self.delegate respondsToSelector:@selector(speakingEvaluationStatusChanged:message:)]) {
        [self.delegate speakingEvaluationStatusChanged:SRSpeakingEvaluationStatusStopRecord message:@"语音测评器-结束录音"];
    }
}

- (void)onCancel {
    
}

- (void)onError:(IFlySpeechError *)errorCode {
    self.resultNotOK = NO;
    if ([self.delegate respondsToSelector:@selector(speakingEvaluationStatusChanged:message:)]) {
        if (errorCode.errorCode == 0) {
            [self.delegate speakingEvaluationStatusChanged:SRSpeakingEvaluationStatusSuccess message:errorCode.errorDesc];
        } else {
            [self.delegate speakingEvaluationStatusChanged:SRSpeakingEvaluationStatusFail message:errorCode.errorDesc];
        }
    }
}

- (void)onResults:(NSData *)results isLast:(BOOL)isLast {
    if (results) {
        NSString *showText = @"";
        
        const char *chResult = (char *)[results bytes];
        
        BOOL isUTF8 = [[self.iflyEvaluator parameterForKey:[IFlySpeechConstant RESULT_ENCODING]] isEqualToString:@"utf-8"];
        NSString *strResults = nil;
        if (isUTF8) {
            strResults = [[NSString alloc] initWithBytes:chResult length:[results length] encoding:NSUTF8StringEncoding];
            if (!strResults) {
                NSStringEncoding encoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
                strResults = [[NSString alloc] initWithData:results encoding:encoding];
            }
        } else {
            NSStringEncoding encoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
            strResults = [[NSString alloc] initWithBytes:chResult length:[results length] encoding:encoding];
        }
        if (strResults) {
            showText = [showText stringByAppendingString:strResults];
        }
        
        if(showText && ![showText isEqualToString:@""]){
            [self.iflyXMLParser parserXml:showText];
        } else {
            if ([self.delegate respondsToSelector:@selector(speakingEvaluationStatusChanged:message:)]) {
                [self.delegate speakingEvaluationStatusChanged:SRSpeakingEvaluationStatusFail message:@"结果解析为空"];
            }
        }
    } else {
        if ([self.delegate respondsToSelector:@selector(speakingEvaluationStatusChanged:message:)]) {
            [self.delegate speakingEvaluationStatusChanged:SRSpeakingEvaluationStatusFail message:@"结果为空"];
        }
    }
}

#pragma mark - ISEResultXmlParserDelegate

- (void)onISEResultXmlParser:(NSXMLParser *)parser Error:(NSError *)error{
    // do nothing
}

- (void)onISEResultXmlParserResult:(ISEResult *)result{
    NSString *resultString = [result toString];
    NSLog(@"result string is %@", resultString);
    
    SREvaluationScoreModel *scoreModel = [[SREvaluationScoreModel alloc] init];
    if (result.is_rejected) {
        scoreModel.isReject = YES;
        scoreModel.originScore = @(0);
        scoreModel.totalScore = @(0);
        scoreModel.accuracyScore = @(0);
        scoreModel.fluencyScore = @(0);
        scoreModel.integrityScore = @(0);
    } else {
        if (self.language == SRSpeakingEvaluationTextTypeEnglish) {
            NSInteger totalScore = round(result.total_score * 20);
            scoreModel.originScore = totalScore > 100 ? @(100) : @(totalScore);
            scoreModel.totalScore = [self bonusTotalScore:@(totalScore)];
            
            NSInteger accuracyScore = round(result.accuracy_score * 20);
            scoreModel.accuracyScore = accuracyScore > 100 ? @(100) : @(accuracyScore);
            
            NSInteger integrityScore = round(result.integrity_score * 20);
            scoreModel.integrityScore = integrityScore > 100 ? @(100) : @(integrityScore);
            
            NSInteger fluencyScore = round(result.fluency_score * 20);
            scoreModel.fluencyScore = fluencyScore > 100 ? @(100) : @(fluencyScore);
            
            NSMutableArray *sentences = result.sentences;
            NSMutableArray *allWords = [[NSMutableArray alloc] init];
            
            [sentences enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                ISEResultSentence *sentence = obj;
                if (sentence.word_count > 0) {
                    [allWords addObjectsFromArray:sentence.words];
                }
            }];
            
            NSMutableArray * __block scoreArray = [[NSMutableArray alloc] init];
            [allWords enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                ISEResultWord *word = obj;
                SREvaluationWordScoreModel *scoreModel = [[SREvaluationWordScoreModel alloc] init];
                scoreModel.word = word.content;
                if (word.dp_message == 0) {
                    scoreModel.score = @(round(word.total_score * 20));
                } else if (word.dp_message == 16) {
                    scoreModel.score = @(0);
                }
                [scoreArray addObject:scoreModel];
            }];
            scoreModel.wordScoreArray = scoreArray;
        } else {
            NSInteger totalScore = round(result.total_score);
            scoreModel.originScore = totalScore > 100 ? @(100) : @(totalScore);
            scoreModel.totalScore = [self bonusTotalScore:@(totalScore)];
            
            CGFloat accuracy = (result.tone_score + result.phone_score) / 2.0f;
            NSInteger accuracyScore = round(accuracy);
            scoreModel.accuracyScore = accuracyScore > 100 ? @(100) : @(accuracyScore);
            
            NSInteger integrityScore = round(result.integrity_score);
            scoreModel.integrityScore = integrityScore > 100 ? @(100) : @(integrityScore);
            
            NSInteger fluencyScore = round(result.fluency_score);
            scoreModel.fluencyScore = fluencyScore > 100 ? @(100) : @(fluencyScore);
        }
    }
    if ([self.delegate respondsToSelector:@selector(speakingEvaluationSuccess:)]) {
        [self.delegate speakingEvaluationSuccess:scoreModel];
    }
}

#pragma mark - parser

- (void)parseChivoxResult:(NSString *)result {
    NSData *stringData = [result dataUsingEncoding:NSUTF8StringEncoding];
    
    NSError *error = nil;
    id jsonDict = [NSJSONSerialization JSONObjectWithData:stringData options:NSJSONReadingMutableContainers error:&error];
    if (error || !jsonDict) {
        if ([self.delegate respondsToSelector:@selector(speakingEvaluationStatusChanged:message:)]) {
            [self.delegate speakingEvaluationStatusChanged:SRSpeakingEvaluationStatusFail message:@"评分失败"];
        }
        return;
    }
    
    NSNumber *errorID = jsonDict[@"errId"];
    if (errorID && [errorID integerValue] > 0) {
        NSString *errorMessage = jsonDict[@"error"];
        if ([self.delegate respondsToSelector:@selector(speakingEvaluationStatusChanged:message:)]) {
            [self.delegate speakingEvaluationStatusChanged:SRSpeakingEvaluationStatusFail message:errorMessage];
        }
        return;
    }
    
    NSDictionary *resultDict = jsonDict[@"result"];
    SREvaluationScoreModel *scoreModel = [[SREvaluationScoreModel alloc] init];
    scoreModel.chivoxToken = jsonDict[@"tokenId"];
    NSNumber *totalScore = resultDict[@"overall"];
    scoreModel.originScore = totalScore;
    scoreModel.totalScore = [self bonusTotalScore:totalScore];
    scoreModel.accuracyScore = resultDict[@"accuracy"];
    scoreModel.integrityScore = resultDict[@"integrity"];
    NSDictionary *fluencyDic = resultDict[@"fluency"];
    scoreModel.fluencyScore = fluencyDic[@"overall"];
    NSArray *wordsArr = resultDict[@"details"];
    NSMutableArray * __block wordScoreArr = [[NSMutableArray alloc] init];
    if (wordsArr && wordsArr.count > 0) {
        [wordsArr enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            NSDictionary *wordDic = obj;
            SREvaluationWordScoreModel *wordScoreModel = [[SREvaluationWordScoreModel alloc] init];
            wordScoreModel.word = wordDic[@"char"];
            wordScoreModel.score = wordDic[@"score"];
            [wordScoreArr addObject:wordScoreModel];
        }];
        scoreModel.wordScoreArray = wordScoreArr;
    }
    
    if ([self.delegate respondsToSelector:@selector(speakingEvaluationSuccess:)]) {
        [self.delegate speakingEvaluationSuccess:scoreModel];
    }
}

- (NSNumber *)bonusTotalScore:(NSNumber *)totalScore {
    if (self.bonusArray && self.bonusArray.count > 0) {
        NSNumber * __block retVal = nil;
        [self.bonusArray enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            SREvaluationScoreBonusModel *model = obj;
            if ([model.lowScore integerValue] <= [totalScore integerValue] && [model.highScore integerValue] >= [totalScore integerValue]) {
                CGFloat finalFloatScore = [totalScore integerValue] * [model.bonus doubleValue];
                NSInteger finalIntegerScore = round(finalFloatScore);
                retVal = @(finalIntegerScore > 100 ? 100 : finalIntegerScore);
                *stop = YES;
            }
        }];
        return retVal ? retVal : totalScore;
    } else {
        return totalScore;
    }
}

@end
